package NetworkManager;



import MiddleLayer.OtherClass.CommandStruct;
import MiddleLayer.OtherClass.MyObservable;
import java.io.*;
import java.net.*;
import java.util.Observer;
import java.util.logging.Level;
import java.util.logging.Logger;

public class MultiThreadChatClient extends Thread implements Runnable{

    int iTimeToSleep = 50;//milis
    MyObservable _observable = new MyObservable();

    // Declaration section
    // clientClient: the client socket
    // os: the output stream
    // is: the input stream

    Socket clientSocket = null;
    PrintStream os = null;
    BufferedReader is = null;
    BufferedReader inputLine = null;
    public boolean closed = false;


    public MultiThreadChatClient()
    {
        this.clientSocket = null;
        this.os = null;
        this.is = null;
    }

    public MultiThreadChatClient(Socket clientSocket, PrintStream os, BufferedReader is, MyObservable _observable)
    {
        this.clientSocket = clientSocket;
        this.os = os;
        this.is = is;
        this._observable = _observable;
    }



    public void connectToServer(String hostName, int port_number)
    {
         // Initialization section:
	// Try to open a socket on a given host and port
	// Try to open input and output streams
	try {
            this.clientSocket = new Socket(hostName, port_number);
            this.inputLine = new BufferedReader(new InputStreamReader(System.in));
            this.os = new PrintStream(clientSocket.getOutputStream());
            this.is = new BufferedReader (new InputStreamReader( clientSocket.getInputStream()));

        } catch (UnknownHostException e) {
            System.err.println("Don't know about host " + hostName);
            this.closed = true;

        } catch (IOException e) {
            System.err.println("Couldn't get I/O for the connection to the host " + hostName);
            this.closed = true;
        }

	// If everything has been initialized then we want to write some data
	// to the socket we have opened a connection to on port port_number
        if (clientSocket.isConnected() && os != null && is != null) {
            
		// Create a thread to read from the server
                new Thread(new MultiThreadChatClient(this.clientSocket, this.os, this.is, this._observable)).start();

        }
     }


    public void run() {
	String responseLine;


	// Keep on reading from the socket till we receive the "Bye" from the server,
	// once we received that then we want to break.
	try{
	    while ((responseLine = is.readLine()) != null) {

                responseLine = is.readLine();

                //Lấy dữ liệu raw text, thông báo cho phía trên biết là có dữ liệu tới
                notifyRawTextHasArrived(responseLine);

                //Kiểm tra xem có phải lệnh thoát hay không?
                if(CommandStruct.isCommandQUIT(responseLine))
                {
                    this.closed = true;
                    break;//thoát khỏi vòng lặp
                }

                System.out.println(responseLine);

                try {
                    this.sleep(iTimeToSleep);
                } catch (InterruptedException ex) {
                    Logger.getLogger(MultiThreadChatClient.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
	} catch (IOException e) {
	    System.err.println("IOException:  " + e);
	}

        if(closed == true)
        {
            if (clientSocket.isConnected() && os != null && is != null) {

                try {


                // Clean up:
		// close the output stream
		// close the input stream
		// close the socket

		os.close();
		is.close();
		clientSocket.close();

                } catch (IOException e) {
                    System.err.println("IOException:  " + e);
                }
            }
        }
    }


    public MyObservable observable() {
        return _observable;
    }

    /*
     * Thông báo cho biết dữ liệu đã được gửi đến
     * Chuyển đổi thành CommandStruct để gửi lên bên trên
     */
    public void notifyRawTextHasArrived(String rawText) {
        CommandStruct cmdStruct = new CommandStruct(rawText);
        this._observable.setChanged();                  // set changed flag
        this._observable.notifyObservers(cmdStruct);    // do notification
    }

    /*
     * Chỉ gọi đến hàm này khi có dữ liệu cần gửi đi
     * Dữ liệu tự động chuyển đổi về kiểu string
     */
    public void sendMessage(CommandStruct cmdStruct) {

        //Vẫn còn kết nối
        if(!this.closed)
        {
            //Biến đổi dữ liệu để gửi đi
            String sendData = cmdStruct.toString();


            //Gửi dữ liệu đi
            os.println(sendData);
            os.flush();


        }
    }

    public synchronized void addObserver(Observer o)
    {
        this._observable.addObserver(o);
    }

}